package com.xuecheng.manage_media.service;

import com.alibaba.fastjson.JSON;
import com.xuecheng.framework.domain.media.MediaFile;
import com.xuecheng.framework.domain.media.response.CheckChunkResult;
import com.xuecheng.framework.domain.media.response.MediaCode;
import com.xuecheng.framework.exception.ExceptionCast;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.framework.model.response.ResponseResult;
import com.xuecheng.manage_media.config.RabbitMQConfig;
import com.xuecheng.manage_media.dao.MediaFileRepository;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.*;

/**
 * @author Administrator
 * @version 1.0
 **/
@Service
public class MediaUploadService {
    @Autowired
    MediaFileRepository mediaFileRepository;

    @Value("${xc-service-manage-media.upload-location}")
    String upload_location;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    //视频处理路由
    @Value("${xc-service-manage-media.mq.routingkey-media-video}")
    public  String routingkey_media_video;


    //文件的位置文件夹
    private String getFileFolderPath(String fileMd5) {
        String fileFolderPath = upload_location + fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) + "/" + fileMd5 + "/";
        return fileFolderPath;
    }

    //得到文件位置
    private String getFilePath(String fileMd5, String fileExt) {

        String fileFolderPath = this.getFileFolderPath(fileMd5);
        String filePath = fileFolderPath + fileMd5 + "." + fileExt;
        return filePath;
    }


    /*注册检查*/
    public ResponseResult register(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) {

        //检查文件在磁盘上是否存在
        //得到文件在磁盘上的路径E:\dev\video\8\5\85e240f87ecc5f81867556b85da0ae70
        String filePath = this.getFilePath(fileMd5, fileExt);
        File file = new File(filePath);
        //检查数据库中是否存在
        Optional<MediaFile> optional = mediaFileRepository.findById(fileMd5);
        //两个都存在,直接返回
        if (file.exists() && optional.isPresent()) {
            return new ResponseResult(MediaCode.CHUNK_FILE_EXIST_CHECK);
        }
        //判断文件夹是否存在,不存在就创建文件夹
        if (!file.exists()) {
            file.mkdirs();
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }

    //分块检查,为了实现断点续传
    public CheckChunkResult checkchunk(String fileMd5, Integer chunk, Integer chunkSize) {
        //得到分块的位置
        String fileFolderPath = this.getFileFolderPath(fileMd5);
        String chunkFileFolderPath = fileFolderPath + "chunk/";
        //判断分块是否存在
        File file = new File(chunkFileFolderPath + chunk);
        if (file.exists()) {
            return new CheckChunkResult(CommonCode.SUCCESS, true);
        } else {
            return new CheckChunkResult(CommonCode.SUCCESS, false);
        }
    }

    /*分块上传*/
    public ResponseResult uploadchunk(MultipartFile file, String fileMd5, Integer chunk) {
        //判断传过来的文件是否存在
        if (file.isEmpty()) {
            ExceptionCast.cast(MediaCode.UPLOAD_FILE_REGISTER_ISNULL);
        }
        //开始上传文件
        //首先得到分块的路径
        String chunkFileFolderPath = this.getFileFolderPath(fileMd5) + "chunk/";
        String chunkFilePath = chunkFileFolderPath + chunk;

        //判断chunk文件夹是否存在
        File chunkFileFolder = new File(chunkFileFolderPath);
        //没有就创建文件夹
        if (!chunkFileFolder.exists()) {
            chunkFileFolder.mkdirs();
        }
        //进行读写操作

        InputStream inputStream = null;
        FileOutputStream outputStream = null;
        try {
            inputStream = file.getInputStream();
            outputStream = new FileOutputStream(new File(chunkFilePath));
            IOUtils.copy(inputStream, outputStream);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }

    /*合并分块*/
    public ResponseResult mergechunks(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) {
//        1）将块文件合并
        String chunkFolderPath = this.getFileFolderPath(fileMd5) + "chunk/";
        File fileChunkFolderPath = new File(chunkFolderPath);
        //得到所有的分块
        List<File> chunkFiles = this.getChunkFiles(fileChunkFolderPath);
        //合并分块
        String filePath = this.getFilePath(fileMd5, fileExt);
        File mergeFile = this.mergeFile(new File(filePath), chunkFiles);
//2）校验文件md5是否正确
        Boolean chack = this.chackMd5(mergeFile, fileMd5);
        if (!chack) {
            ExceptionCast.cast(MediaCode.MERGE_FILE_CHECKFAIL);
        }
//3）向Mongodb写入文件信息
        //3、将文件的信息写入mongodb
        MediaFile mediaFile = new MediaFile();
        mediaFile.setFileId(fileMd5);
        mediaFile.setFileOriginalName(fileName);
        mediaFile.setFileName(fileMd5 + "." + fileExt);
        //文件路径保存相对路径
        String filePath1 = fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) + "/" + fileMd5 + "/";
        mediaFile.setFilePath(filePath1);
        mediaFile.setFileSize(fileSize);
        mediaFile.setUploadTime(new Date());
        mediaFile.setMimeType(mimetype);
        mediaFile.setFileType(fileExt);
        //状态为上传成功
        mediaFile.setFileStatus("301002");
        mediaFileRepository.save(mediaFile);
        //向mq发送消息
        return this.sendProcessVideoMsg(fileMd5);

    }

    //获取所有的文件
    private List<File> getChunkFiles(File chunkfileFolder) {
        //得到文件夹下所有的文件
        File[] files = chunkfileFolder.listFiles();
        //放进集合中进行排序
        List<File> list = new ArrayList();
        list.addAll(Arrays.asList(files));
        Collections.sort(list, new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                if (Integer.parseInt(o1.getName()) > Integer.parseInt(o2.getName())) {
                    return 1;
                } else return -1;
            }
        });
        return list;
    }

    //合并分块
    private File mergeFile(File mergeFile, List<File> files) {

        try {
            //得到分块的路径
            if (mergeFile.exists()) {
                //如果有就删除
                mergeFile.delete();
            } else {
                //没有创建新的文件
                mergeFile.createNewFile();
            }
            RandomAccessFile raf_write = new RandomAccessFile(mergeFile, "rw");
            //将文件进行合并
            byte[] bytes = new byte[1024];
            for (File file : files) {
                RandomAccessFile raf_read = new RandomAccessFile(file, "r");
                int len = -1;

                while ((len = raf_read.read(bytes)) != -1) {
                    raf_write.write(bytes, 0, len);
                }
                raf_read.close();
            }
            raf_write.close();

            return mergeFile;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    //校验Md5
    private Boolean chackMd5(File file, String fileMd5) {
        if (file == null || StringUtils.isEmpty(fileMd5)) {
            return false;
        }
        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream(file);
            String md5Hex = DigestUtils.md5Hex(fileInputStream);
            if (md5Hex.equals(fileMd5)) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;

    }

    //向mq发送消息
    public ResponseResult sendProcessVideoMsg(String mediaId) {
        //首先进行查询,如果存在就发送消息,不存在直接返回
        Optional<MediaFile> optional = mediaFileRepository.findById(mediaId);
        if (!optional.isPresent()) {
            return new ResponseResult(CommonCode.FAIL);
        }
        Map<String, String> mapMsg = new HashMap<>();
        mapMsg.put("mediaId", mediaId);
        String msg = JSON.toJSONString(mapMsg);
        try {
            this.rabbitTemplate.convertAndSend(RabbitMQConfig.EX_MEDIA_PROCESSTASK, routingkey_media_video, msg);
        } catch (AmqpException e) {
            e.printStackTrace();
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }
}
